﻿using RWSBeeldbank.Common;
using RWSBeeldbank.Utility;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Resources;
using Windows.ApplicationModel.Resources.Core;
using Windows.Data.Json;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using RWSBeeldbank.DataModel;

// The data model defined by this file serves as a representative example of a strongly-typed 
// model that supports notification when members are added, removed, or modified.  The property
// names chosen coincide with data bindings in the standard item templates.
//
// Applications may use this model as a starting point and build on it, or discard it entirely and
// replace it with something appropriate to their needs.

namespace RWSBeeldbank.Data
{
    /// <summary>
    /// Creates a collection of groups and items.
    /// </summary>
    public sealed class MediaObjectDataSource
    {
        public static MediaObjectDataSource _mediaObjectDataSource = new MediaObjectDataSource();

        // DynamicLoadItems is a subset if items that is filled while scrolling. This collection is never bigger than 20 items   
        private DynamicLoadItems _dynamicLoadItems = new DynamicLoadItems();
        private DynamicLoadItemsSearch _dynamicLoadItemsSearch = new DynamicLoadItemsSearch();
                
        private static List<string> _allKeyWordsList = new List<string>();
        public static string[] AllKeywords
        {
            get { return _allKeyWordsList.ToArray(); }
        }

        private ObservableCollection<MediaObjectDataGroup> _allGroups = new ObservableCollection<MediaObjectDataGroup>();
        public ObservableCollection<MediaObjectDataGroup> AllGroups
        {
            get { return this._allGroups; }
            set { this._allGroups = AllGroups;  }
        }

        public DynamicLoadItems DynamicLoadItems(int collectionId)
        {
            _dynamicLoadItems.Clear();

            this._dynamicLoadItems.resetCurrentPage();
            this._dynamicLoadItems.setGroupId(collectionId);
            return this._dynamicLoadItems;
        }

        public DynamicLoadItemsSearch DynamicLoadItemsSearch(string searchString)
        {
            _dynamicLoadItemsSearch.Clear();

            this._dynamicLoadItemsSearch.resetCurrentPage();
            this._dynamicLoadItemsSearch.setSearchString(searchString);
            return this._dynamicLoadItemsSearch;
        }



        // Clear memory in order to be able to reload the app with a new value for TypeId
        public static void Clear()
        {
            _mediaObjectDataSource = new MediaObjectDataSource();
        }

        public static async Task<ObservableCollection<MediaObjectDataItem>> GetTitles(int numResults, int page, int collectionId)
        {
            // Determine Media Type Id
            int typeId = Constants.ITEM_TYPE_DIGITAL;
            if (ApplicationData.Current.RoamingSettings.Values.ContainsKey("MediaType"))
                typeId = Convert.ToInt16(ApplicationData.Current.RoamingSettings.Values["MediaType"]);

            var JsonItem = await Utility.Tools.cachedFeed(new Uri(Constants.FEEDBASE + "/api/W8_ThemeMediaList?collectionId=" + collectionId + "&typeId=" + typeId + "&aantalResultaten=" + numResults + "&orderBy=7&skip=" + page));

            // TODO: geen internet afvangen

            CreateThemeItems(JsonItem, typeId);

            // Append the items of the specific group to the loading gridview
            return _mediaObjectDataSource.AllGroups.FirstOrDefault(c => c.UniqueId.Equals(collectionId.ToString())).Items;
        }

        public static async Task<ObservableCollection<MediaObjectDataItem>> GetTitlesSearch(int numResults, int skip, string searchString)
        {
            // Determine Media Type Id
            int typeId = Constants.ITEM_TYPE_DIGITAL;
            if (ApplicationData.Current.RoamingSettings.Values.ContainsKey("MediaType"))
                typeId = Convert.ToInt16(ApplicationData.Current.RoamingSettings.Values["MediaType"]);

            var JsonItem = await Utility.Tools.cachedFeed(new Uri(Constants.FEEDBASE + "/api/W8_MetroSimpleSearch?searchString=" + searchString + "&typeId=" + typeId + "&aantalResultaten=" + numResults + "&orderBy=7&skip=" + skip));

            // create items in a search collection
            CreateThemeItems(JsonItem, typeId, true);

            // Append the items of the specific group to the loading gridview
            return _mediaObjectDataSource.AllGroups.FirstOrDefault(c => c.UniqueId.Equals(Constants.SEARCHCOLLECTIONID)).Items;
        }

        public static IEnumerable<MediaObjectDataGroup> GetGroups(string uniqueId)
        {
            if (!uniqueId.Equals("AllGroups")) throw new ArgumentException("Only 'AllGroups' is supported as a collection of groups");

            return _mediaObjectDataSource.AllGroups.Where(p => p.UniqueId.Equals(Constants.SEARCHCOLLECTIONID) != true);
        }

        public static MediaObjectDataGroup GetGroup(string uniqueId)
        {
            // Simple linear search is acceptable for small data sets
            var matches = _mediaObjectDataSource.AllGroups.Where(group => group.UniqueId.Equals(uniqueId));
            if (matches.Count() == 1) return matches.First();
            return null;
        }

        public static MediaObjectDataItem GetItem(string uniqueId)   
        {
            string groupId = Tools.GetGroupId(uniqueId);

            if (groupId == null)
            {
                var matches = _mediaObjectDataSource.AllGroups.SelectMany(group => group.Items);
                var item = from b in matches
                           where b.Group.UniqueId == groupId
                           &&
                           b.UniqueId == uniqueId
                           select b;

                return item.FirstOrDefault();

            }
            else
            {
                // Simple linear search is acceptable for small data sets
                return _mediaObjectDataSource.AllGroups.SelectMany(group => group.Items).Where(item => item.UniqueId.Equals(uniqueId)).FirstOrDefault();
            }

        }

        public static async Task LoadMoreItemsAsync(int collectionId, int currentItemIndex)
        {
            var items = await MediaObjectDataSource.GetTitles(Constants.FLIPVIEW_PAGE_SIZE, currentItemIndex, collectionId);

            foreach (var item in items)
            {
                item.Group = MediaObjectDataSource._mediaObjectDataSource.AllGroups.FirstOrDefault(c => c.UniqueId.Equals(collectionId.ToString()));
                _mediaObjectDataSource._dynamicLoadItems.Add(item);
            }         
        }
        
        public static async Task LoadRemoteDataAsync()
        {
            await LoadRemoteDataAsync("");
        }

        public static async Task LoadRemoteDataAsync(String searchString)
        {
            // Retrieve mediaObject data from Beeldbank webserver

            // Determine Media Type Id
            int typeId = Constants.ITEM_TYPE_DIGITAL;
            if (ApplicationData.Current.RoamingSettings.Values.ContainsKey("MediaType"))
                typeId = Convert.ToInt16(ApplicationData.Current.RoamingSettings.Values["MediaType"]);

            var mediaObjects = await Utility.Tools.cachedFeed(new Uri(Constants.FEEDBASE + "/api/W8_TopMediaList?searchString=" + searchString + "&typeId=" + typeId + "&aantalResultaten=6"));
            var groupThemes = await Utility.Tools.cachedFeed(new Uri(Constants.FEEDBASE + "/api/W8_ThemesList?typeId=" + typeId));

            if (typeId == Constants.ITEM_TYPE_DIGITAL)
            {
                var newMediaThemes = await Utility.Tools.cachedFeed(new Uri(Constants.FEEDBASE + "/api/W8_NewMediaList"));

                // Convert the JSON objects into MediaObjectDataItems and MediaObjectDataGroups
                CreateMediaObjectsAndMediaObjectGroups(mediaObjects, groupThemes, newMediaThemes, typeId);
            }
            else
            {
                // Convert the JSON objects into MediaObjectDataItems and MediaObjectDataGroups
                CreateMediaObjectsAndMediaObjectGroups(mediaObjects, groupThemes, typeId);
            }
        }

        public static async Task LoadLocalDataAsync()
        {
            // Retrieve mediaObject data from MediaObjects.txt
            var file = await Package.Current.InstalledLocation.GetFileAsync("Data\\RijkswaterstaatFull.txt"); // Data\\MediaObjects.txt
            var result = await FileIO.ReadTextAsync(file, UnicodeEncoding.Utf8);

            // Parse the JSON mediaObject data
            var mediaObjects = JsonArray.Parse(result);

            // Retrieve mediaObject data from MediaObjects.txt
            var groupFile = await Package.Current.InstalledLocation.GetFileAsync("Data\\ThemesList.txt"); // Data\\MediaObjects.txt
            var groupResult = await FileIO.ReadTextAsync(groupFile, UnicodeEncoding.Utf8);

            // Parse the JSON mediaObject data
            var groupThemes = JsonArray.Parse(groupResult);

            // Convert the JSON objects into MediaObjectDataItems and MediaObjectDataGroups and set the typeId to be of digital images
            CreateMediaObjectsAndMediaObjectGroups(mediaObjects, groupThemes, null, 19);
        }

        // Add item keywords to Suggestion List
        private static void AddKeywordsToSuggestionList(string keywordsString)
        {
            String[] keywords = keywordsString.Split('|');

            foreach (string x in keywords)
            {
                if (!_allKeyWordsList.Exists(p => p == x.Trim().ToLower()))
                {
                    _allKeyWordsList.Add(x.Trim().ToLower());
                }
            }
        }

        public static async Task SetRatingAsync(int mediaObjectId, int rating)
        {
            // Retrieve mediaObject data from Beeldbank webserver
            var client = new HttpClient();
            client.MaxResponseContentBufferSize = Constants.MAX_RESPONSE_BUFFERSIZE;
                        
            var response = await client.GetAsync(new Uri(Constants.FEEDBASE + "/api/W8_RateMediaObject?rating=" + rating + "&mediaObjectId=" + mediaObjectId));

            var result = await response.Content.ReadAsStringAsync();

            Tools.markItemAsRated(mediaObjectId, Convert.ToInt32(rating));
        }
        
        // AB: TODO check if this and the CreateMediaObjectsAndMediaObjectGroups(JsonArray array, JsonArray groupsArray) can be combined
        private static void CreateThemeItems(JsonArray array, int typeId, bool? isSearchGroup = false)
        {
            foreach (var item in array)
            {
                var obj = item.GetObject();
                MediaObjectDataItem mediaObject = new MediaObjectDataItem();

                foreach (var key in obj.Keys)
                {
                    IJsonValue val;
                    if (!obj.TryGetValue(key, out val))
                        continue;

                    if (val.ValueType == JsonValueType.Null)
                        continue;

                    switch (key)
                    {
                        case "alttitle":
                            mediaObject.AltTitle = val.GetString();
                            break;
                        case "author":
                            mediaObject.Author = val.GetString();
                            break;
                        case "datecreated":
                            mediaObject.DateCreated = val.GetString();
                            break;
                        case "description":
                            mediaObject.Description = val.GetString();
                            break;
                        case "filenumber":
                            mediaObject.FileNumber = (int)val.GetNumber();
                            break;
                        case "googlezoom":
                            mediaObject.GoogleZoom = (int)val.GetNumber();
                            break;
                        case "gps":
                            mediaObject.GPS = val.GetString();
                            break;
                        case "height":
                            mediaObject.Height = (int)val.GetNumber();
                            break;
                        case "image":
                            mediaObject.SetLargeImage(val.GetString());

                            if (typeId == Constants.ITEM_TYPE_DIGITAL)
                                mediaObject.SetTileImage(val.GetString(), Constants.SRC_LOCATION_PHOTO);
                            else
                                if (typeId == Constants.ITEM_TYPE_ANALOG)
                                    mediaObject.SetTileImage(val.GetString(), Constants.SRC_LOCATION_PHOTO, Constants.TILE_SIZE_ANALOG);
                                else
                                    mediaObject.SetTileImage(val.GetString(), Constants.SRC_LOCATION_VIDEO);

                            break;
                        case "key":
                            mediaObject.Id = (int)val.GetNumber();
                            mediaObject.DirectLink = Constants.DIRECTLINKBASE + mediaObject.Id.ToString();
                            break;
                        case "keywords":
                            mediaObject.Keywords = val.GetString();
                            AddKeywordsToSuggestionList(mediaObject.Keywords);
                            break;
                        case "latitude":
                            mediaObject.Latitude = val.GetNumber();
                            break;
                        case "location":
                            mediaObject.Location = val.GetString();
                            break;
                        case "longitude":
                            mediaObject.Longitude = val.GetNumber();
                            break;
                        case "title":
                            mediaObject.Title = val.GetString();
                            break;
                        case "rating":
                            mediaObject.Rating = val.GetString();
                            break;
                        case "videolength":
                            mediaObject.VideoLength = val.GetString();
                            break;
                        case "videoformat":
                            mediaObject.VideoFormat = val.GetString();
                            break;
                        case "searchResultSetSize":
                            mediaObject.SearchResultSetSize = (int)val.GetNumber();
                            break;
                        case "group":
                            // Get the group of this MediaItem
                            var mediaObjectGroup = val.GetObject();

                            // Get the key from the group of this media item
                            IJsonValue groupKey;
                            if (!mediaObjectGroup.TryGetValue("CollectionId", out groupKey))
                                continue;

                            if (isSearchGroup == true)
                            {
                                mediaObject.Group = _mediaObjectDataSource.AllGroups.FirstOrDefault(c => c.UniqueId.Equals(Constants.SEARCHCOLLECTIONID));
                            }
                            else
                            {
                                mediaObject.Group = _mediaObjectDataSource.AllGroups.FirstOrDefault(c => c.UniqueId.Equals(((int)groupKey.GetNumber()).ToString()));
                            }

                            break;
                    }
                }
                
                // Determine Unique ID
                Tools.CreateUniqueIdCombination(mediaObject);

                bool isInCollection = false;
                foreach (var existingItem in _mediaObjectDataSource.AllGroups.FirstOrDefault(c => c.UniqueId.Equals(mediaObject.Group.UniqueId)).Items) 
                {
                    if (existingItem.UniqueId.Equals(mediaObject.UniqueId))
                    {
                        isInCollection = true;
                        break;
                    }
                }

                if (!isInCollection)
                {
                    // Determine GPS string
                    Tools.DetermineGPS(mediaObject);

                    mediaObject.TypeId = typeId;
                    _mediaObjectDataSource.AllGroups.FirstOrDefault(c => c.UniqueId.Equals(mediaObject.Group.UniqueId)).Items.Add(mediaObject);
                }
            }
        }

        private static void CreateMediaObjectsAndMediaObjectGroups(JsonArray mediaObjectArray, JsonArray groupsArray, int typeId)
        {
            CreateMediaObjectsAndMediaObjectGroups(mediaObjectArray, groupsArray, null, typeId);
        }

        private static void CreateMediaObjectsAndMediaObjectGroups(JsonArray mediaObjectArray, JsonArray groupsArray, JsonArray newMediaObjectArray, int typeId)
        {
            // Only add New Media Today when present
            if ((newMediaObjectArray != null) && (newMediaObjectArray.Count() > 0))
            {
                MediaObjectDataGroup mediaObjectGroup = new MediaObjectDataGroup(
                    Constants.NEWCOLLECTIONID,
                    Constants.NEWCOLLECTIONNAME,
                    Constants.NEWCOLLECTIONIMAGEPATH,
                    Constants.NEWCOLLECTIONDESCRIPTION,
                    newMediaObjectArray.Count());

                _mediaObjectDataSource.AllGroups.Add(mediaObjectGroup);
            }

            // Create search collection
            MediaObjectDataGroup mediaObjectSearchGroup = new MediaObjectDataGroup(
                Constants.SEARCHCOLLECTIONID,
                Constants.SEARCHCOLLECTIONNAME,
                Constants.SEARCHCOLLECTIONIMAGEPATH,
                Constants.SEARCHCOLLECTIONDESCRIPTION,
                0);
            _mediaObjectDataSource.AllGroups.Add(mediaObjectSearchGroup);


            foreach (var item in groupsArray)
            {
                var obj = item.GetObject();

                String uniqueId = "";
                String title = "";
                String themeImagePath = "";
                String description = "";
                int mediaObjectsCount = 0;
                
                Uri _baseUri = new Uri("ms-appx:///");

                foreach (var key in obj.Keys)
                {
                    IJsonValue val;
                    if (!obj.TryGetValue(key, out val))
                        continue;

                    if (val.ValueType == JsonValueType.Null)
                        continue;

                    switch (key)
                    {
                        case "CollectionId":
                            uniqueId = val.GetNumber().ToString();
                            break;
                        case "CollectionName":
                            title = val.GetString();
                            break;
                        case "CollectionDescription":
                            description = val.GetString();
                            break;
                        case "CollectionCount":
                            mediaObjectsCount = (int)val.GetNumber();
                            break;
                        case "CollectionImage":
                            themeImagePath = "ms-appx:///Images/" + uniqueId.ToString() + ".jpg";
                            break;
                    }
                }

                MediaObjectDataGroup mediaObjectGroup = new MediaObjectDataGroup(uniqueId, title, themeImagePath, description, mediaObjectsCount);

                _mediaObjectDataSource.AllGroups.Add(mediaObjectGroup);
            }

            AddMediaToMasterList(mediaObjectArray, typeId, false);

            // Only add New Media Today when present
            if ((newMediaObjectArray != null) && (newMediaObjectArray.Count() > 0))
                AddMediaToMasterList(newMediaObjectArray, typeId, true);
        }

        private static void AddMediaToMasterList(JsonArray mediaObjectArray, int typeId, bool newMedia)
        {
            foreach (var item in mediaObjectArray)
            {
                var obj = item.GetObject();
                MediaObjectDataItem mediaObject = new MediaObjectDataItem();
                foreach (var key in obj.Keys)
                {
                    IJsonValue val;
                    if (!obj.TryGetValue(key, out val))
                        continue;

                    if (val.ValueType == JsonValueType.Null)
                        continue;

                    switch (key)
                    {
                        case "alttitle":
                            mediaObject.AltTitle = val.GetString();
                            break;
                        case "author":
                            mediaObject.Author = val.GetString();
                            break;
                        case "datecreated":
                            mediaObject.DateCreated = val.GetString();
                            break;
                        case "description":
                            mediaObject.Description = val.GetString();
                            break;
                        case "filenumber":
                            mediaObject.FileNumber = (int)val.GetNumber();
                            break;
                        case "googlezoom":
                            mediaObject.GoogleZoom = (int)val.GetNumber();
                            break;
                        case "height":
                            mediaObject.Height = (int)val.GetNumber();
                            break;
                        case "gps":
                            mediaObject.GPS = val.GetString();
                            break;
                        case "image":
                            mediaObject.SetLargeImage(val.GetString());

                            if (typeId == Constants.ITEM_TYPE_DIGITAL)
                                mediaObject.SetTileImage(val.GetString(), Constants.SRC_LOCATION_PHOTO);
                            else
                                if (typeId == Constants.ITEM_TYPE_ANALOG)
                                    mediaObject.SetTileImage(val.GetString(), Constants.SRC_LOCATION_PHOTO, Constants.TILE_SIZE_ANALOG);
                                else
                                    mediaObject.SetTileImage(val.GetString(), Constants.SRC_LOCATION_VIDEO);

                            break;
                        case "key":
                            mediaObject.Id = (int)val.GetNumber();
                            mediaObject.DirectLink = Constants.DIRECTLINKBASE + mediaObject.Id.ToString();
                            break;
                        case "keywords":
                            mediaObject.Keywords = val.GetString();
                            AddKeywordsToSuggestionList(mediaObject.Keywords);
                            break;
                        case "latitude":
                            mediaObject.Latitude = val.GetNumber();
                            break;
                        case "location":
                            mediaObject.Location = val.GetString();
                            break;
                        case "longitude":
                            mediaObject.Longitude = val.GetNumber();
                            break;
                        case "title":
                            mediaObject.Title = val.GetString();
                            break;
                        case "rating":
                            mediaObject.Rating = val.GetString();
                            break;
                        case "videolength":
                            mediaObject.VideoLength = val.GetString();
                            break;
                        case "videoformat":
                            mediaObject.VideoFormat = val.GetString();
                            break;
                        case "searchResultSetSize":
                            mediaObject.SearchResultSetSize = (int)val.GetNumber();
                            break;
                        case "group":
                            // Get the group of this MediaItem
                            var mediaObjectGroup = val.GetObject();

                            // Get the key from the group of this media item
                            IJsonValue groupKey;
                            if (!mediaObjectGroup.TryGetValue("CollectionId", out groupKey))
                                continue;

                            if (newMedia)
                                mediaObject.Group = _mediaObjectDataSource.AllGroups.FirstOrDefault(c => c.UniqueId.Equals(Constants.NEWCOLLECTIONID));
                            else
                                mediaObject.Group = _mediaObjectDataSource.AllGroups.FirstOrDefault(c => c.UniqueId.Equals(((int)groupKey.GetNumber()).ToString()));

                            // Temporary solution in order to ensure that search items have a group
                            if (mediaObject.Group == null)
                            {
                                mediaObject.Group = _mediaObjectDataSource.AllGroups.FirstOrDefault(c => c.UniqueId.Equals(Constants.SEARCHCOLLECTIONID));
                            }

                            break;
                    }
                }
                // All fields have been processed

                // Determine GPS
                Tools.DetermineGPS(mediaObject);

                // Determine Unique ID
                Tools.CreateUniqueIdCombination(mediaObject);
                
                // Check if items has not already been added
                if (mediaObject.Group.Items.FirstOrDefault(m => m.UniqueId.Equals(mediaObject.UniqueId)) == null)
                {
                    mediaObject.TypeId = typeId;
                    // select the group and add this item to the items in this group
                    mediaObject.Group.Items.Add(mediaObject);
                }
            }
        }
    }
}